#include "lwip/opt.h"
#include "stm32f4x7_eth.h"
#include "dp83848_bsp.h"
#include "netif.h"
#include "netconf.h"
#include "lwip/dhcp.h"

/* FreeRTOS 相关头文件 */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"


/* 任务堆栈大小和优先级定义 */
#define ETH_LINK_TASK_STACK_SIZE		( configMINIMAL_STACK_SIZE * 2 ) // 任务堆栈大小
#define ETH_LINK_TASK_PRIORITY		    ( tskIDLE_PRIORITY + 3 )        // 任务优先级
#define emacBLOCK_TIME_WAITING_ETH_LINK_IT	( ( portTickType ) 100 )      // 等待中断信号量的阻塞时间


/* 全局变量 */
ETH_InitTypeDef ETH_InitStructure;
__IO uint32_t  EthStatus = 0;        // 以太网状态
extern struct netif xnetif;          // LwIP 网络接口
#ifdef USE_DHCP
extern __IO uint8_t DHCP_state;      // DHCP 状态
#endif
xSemaphoreHandle ETH_link_xSemaphore = NULL; // 用于以太网链路中断的信号量


/* 静态函数声明 */
static void ETH_GPIO_Config(void);     // 配置以太网相关 GPIO
static void ETH_NVIC_Config(void);     // 配置 NVIC 中断
static void ETH_MACDMA_Config(void);   // 配置 MAC 和 DMA


/**
  * @brief  以太网 BSP 初始化函数
  * @param  None
  * @retval None
  */
void ETH_BSP_Config(void)
{
  /* 1. 配置以太网 GPIO 引脚（RMII/MII 接口） */
  ETH_GPIO_Config();

  /* 2. 配置 NVIC 以太网中断优先级 */
  ETH_NVIC_Config();

  /* 3. 配置 MAC 和 DMA 控制器 */
  ETH_MACDMA_Config();

  /* 4. 读取 PHY 状态寄存器，判断当前是否连接 */
  if(ETH_ReadPHYRegister(DP83848_PHY_ADDRESS, PHY_SR) & 1)
  {
    EthStatus |= ETH_LINK_FLAG; // 如果连接，设置链路标志
  }

  /* 5. 配置 PHY 当链路状态变化时产生中断 */
  Eth_Link_PHYITConfig(DP83848_PHY_ADDRESS);

  /* 6. 配置 EXTI 用于接收 PHY 链路状态中断 */
  Eth_Link_EXTIConfig();
  
  /* 7. 创建用于以太网链路处理的二值信号量 */
  if (ETH_link_xSemaphore == NULL)
  {
    vSemaphoreCreateBinary( ETH_link_xSemaphore );
  }

  /* 8. 创建处理以太网链路状态变化的任务 */
  xTaskCreate(Eth_Link_IT_task,           // 任务函数
              (signed char*) "E_link",    // 任务名
              ETH_LINK_TASK_STACK_SIZE,   // 堆栈大小
              (void *)DP83848_PHY_ADDRESS,// 传递 PHY 地址作为参数
              ETH_LINK_TASK_PRIORITY,     // 优先级
              NULL);                      // 任务句柄
}


/**
  * @brief  配置 MAC 和 DMA 控制器
  * @param  None
  * @retval None
  */
static void ETH_MACDMA_Config(void)
{ 
  /* 使能以太网 MAC 和 DMA 时钟 */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_ETH_MAC | 
                         RCC_AHB1Periph_ETH_MAC_Tx |
                         RCC_AHB1Periph_ETH_MAC_Rx, ENABLE);

  /* 复位以太网控制器 */
  ETH_DeInit();

  /* 软件复位 MAC/DMA */
  ETH_SoftwareReset();

  /* 等待软件复位完成 */
  while (ETH_GetSoftwareResetStatus() == SET);

  /* 初始化以太网配置结构体 */
  ETH_StructInit(&ETH_InitStructure);

  /*------------------------   MAC 层配置   -------------------------------*/
  ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable; // 自动协商速率和双工模式
  // 如果禁用自动协商，需要手动设置速率和双工模式
  // ETH_InitStructure.ETH_Speed = ETH_Speed_10M;
  // ETH_InitStructure.ETH_Mode = ETH_Mode_FullDuplex;

  ETH_InitStructure.ETH_LoopbackMode = ETH_LoopbackMode_Disable;  // 禁用回环模式
  ETH_InitStructure.ETH_RetryTransmission = ETH_RetryTransmission_Disable; // 禁用重传
  ETH_InitStructure.ETH_AutomaticPadCRCStrip = ETH_AutomaticPadCRCStrip_Disable; // 禁用自动 CRC 剥离
  ETH_InitStructure.ETH_ReceiveAll = ETH_ReceiveAll_Disable;      // 不接收所有帧
  ETH_InitStructure.ETH_BroadcastFramesReception = ETH_BroadcastFramesReception_Enable; // 接收广播帧
  ETH_InitStructure.ETH_PromiscuousMode = ETH_PromiscuousMode_Disable; // 禁用混杂模式
  ETH_InitStructure.ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_Perfect; // 精确过滤组播帧
  ETH_InitStructure.ETH_UnicastFramesFilter = ETH_UnicastFramesFilter_Perfect; // 精确过滤单播帧

#ifdef CHECKSUM_BY_HARDWARE
  ETH_InitStructure.ETH_ChecksumOffload = ETH_ChecksumOffload_Enable; // 启用硬件校验和
#endif

  /*------------------------   DMA 层配置   -------------------------------*/  
  /* 启用丢弃校验和错误帧 */
  ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Enable;
  /* 启用接收和发送的存储转发模式 */
  ETH_InitStructure.ETH_ReceiveStoreForward = ETH_ReceiveStoreForward_Enable;
  ETH_InitStructure.ETH_TransmitStoreForward = ETH_TransmitStoreForward_Enable;
 
  /* 禁用转发错误帧和小于最小长度的好帧 */
  ETH_InitStructure.ETH_ForwardErrorFrames = ETH_ForwardErrorFrames_Disable;
  ETH_InitStructure.ETH_ForwardUndersizedGoodFrames = ETH_ForwardUndersizedGoodFrames_Disable;
  
  /* 使能第二帧处理和地址对齐传输 */
  ETH_InitStructure.ETH_SecondFrameOperate = ETH_SecondFrameOperate_Enable;
  ETH_InitStructure.ETH_AddressAlignedBeats = ETH_AddressAlignedBeats_Enable;
  
  /* DMA 突发长度配置 */
  ETH_InitStructure.ETH_FixedBurst = ETH_FixedBurst_Enable;
  ETH_InitStructure.ETH_RxDMABurstLength = ETH_RxDMABurstLength_32Beat;
  ETH_InitStructure.ETH_TxDMABurstLength = ETH_TxDMABurstLength_32Beat;
  
  /* DMA 仲裁模式：接收:发送 = 2:1 */
  ETH_InitStructure.ETH_DMAArbitration = ETH_DMAArbitration_RoundRobin_RxTx_2_1;

  /* 配置并启动以太网 MAC/DMA */
  EthStatus = ETH_Init(&ETH_InitStructure, DP83848_PHY_ADDRESS);
  
  /* 使能以太网接收中断和正常中断 */
  ETH_DMAITConfig(ETH_DMA_IT_NIS | ETH_DMA_IT_R, ENABLE);
}


/**
  * @brief  配置以太网接口所需的所有GPIO端口
  * @note   支持MII（多线接口）和RMII（简化多线接口）两种模式，通过宏定义切换
  * @param  None
  * @retval None
  */
void ETH_GPIO_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure; // GPIO初始化结构体

  /* 1. 使能以太网相关GPIO端口的时钟 */
  // 涉及端口：A/B/C/F/G/H/I（根据MII/RMII模式的引脚需求启用）
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB |
                         RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOI |
                         RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOH |
                         RCC_AHB1Periph_GPIOF, ENABLE);

  /* 2. 使能SYSCFG时钟（用于MII/RMII模式选择和EXTI引脚映射） */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
 
  /* 3. 配置MCO引脚（PA8）——用于为PHY提供时钟（仅MII模式可能需要） */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;          // 目标引脚：PA8（MCO输出）
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; // 引脚速度：100MHz（高速）
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;       // 模式：复用功能（AF）
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;     // 输出类型：推挽（增强驱动能力）
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;  // 上下拉：无（避免干扰时钟信号）
  GPIO_Init(GPIOA, &GPIO_InitStructure);             // 应用配置到GPIOA
  
  /* 4. MII/RMII模式选择（通过宏定义切换，决定硬件接口类型） */
#ifdef MII_MODE /* 若启用MII模式（STM324xx-EVAL开发板默认） */
 #ifdef PHY_CLOCK_MCO /* 若需要通过MCO为PHY提供时钟 */

  /* 配置MCO1输出HSE时钟（25MHz）到PA8，用于驱动PHY的时钟输入 */
  // RCC_MCO1Source_HSE：时钟源为外部高速晶振（HSE，通常25MHz）
  // RCC_MCO1Div_1：不分频（直接输出25MHz，符合PHY时钟需求）
  RCC_MCO1Config(RCC_MCO1Source_HSE, RCC_MCO1Div_1);
 #endif /* PHY_CLOCK_MCO */

  /* 配置SYSCFG为MII媒体接口模式 */
  SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_MII);
#elif defined RMII_MODE  /* 若启用RMII模式（简化接口，引脚更少） */

  /* 配置SYSCFG为RMII媒体接口模式 */
  SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_RMII);
#endif
  
/* 5. 以太网引脚详细配置（MII和RMII模式共用部分引脚，差异通过模式选择自动适配） */
   /* 引脚功能映射说明（关键！对应硬件接线和PHY通信）：
        ETH_MDIO -------------------------> PA2    （PHY管理数据输入输出）
        ETH_MDC --------------------------> PC1    （PHY管理时钟，由MCU输出）
        ETH_PPS_OUT ----------------------> PB5    （脉冲 per second 输出，可选）
        ETH_MII_CRS ----------------------> PH2    （MII模式：载波检测）
        ETH_MII_COL ----------------------> PH3    （MII模式：冲突检测）
        ETH_MII_RX_ER --------------------> PI10   （MII模式：接收错误指示）
        ETH_MII_RXD2 ---------------------> PH6    （MII模式：接收数据2）
        ETH_MII_RXD3 ---------------------> PH7    （MII模式：接收数据3）
        ETH_MII_TX_CLK -------------------> PC3    （MII模式：发送时钟，由PHY提供）
        ETH_MII_TXD2 ---------------------> PC2    （MII模式：发送数据2）
        ETH_MII_TXD3 ---------------------> PB8    （MII模式：发送数据3）
        ETH_MII_RX_CLK/ETH_RMII_REF_CLK---> PA1    （MII：接收时钟；RMII：参考时钟）
        ETH_MII_RX_DV/ETH_RMII_CRS_DV ----> PA7    （MII：接收数据有效；RMII：载波/数据有效）
        ETH_MII_RXD0/ETH_RMII_RXD0 -------> PC4    （MII/RMII：接收数据0）
        ETH_MII_RXD1/ETH_RMII_RXD1 -------> PC5    （MII/RMII：接收数据1）
        ETH_MII_TX_EN/ETH_RMII_TX_EN -----> PG11   （MII/RMII：发送使能）
        ETH_MII_TXD0/ETH_RMII_TXD0 -------> PG13   （MII/RMII：发送数据0）
        ETH_MII_TXD1/ETH_RMII_TXD1 -------> PG14   （MII/RMII：发送数据1）
   */

  /* 配置GPIOA引脚：PA1（时钟）、PA2（MDIO）、PA7（数据有效） */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_7;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  // 将引脚复用为ETH功能（AF_ETH是以太网外设的复用编号）
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_ETH); // PA1 -> ETH时钟
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_ETH); // PA2 -> ETH_MDIO
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_ETH); // PA7 -> 数据有效

  /* 配置GPIOB引脚：PB5（PPS）、PB8（MII_TXD3） */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_8;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_ETH); // PB5 -> ETH_PPS_OUT
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_ETH); // PB8 -> MII_TXD3

  /* 配置GPIOC引脚：PC1（MDC）、PC2（MII_TXD2）、PC3（MII_TX_CLK）、PC4（RXD0）、PC5（RXD1） */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
  GPIO_Init(GPIOC, &GPIO_InitStructure);
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource1, GPIO_AF_ETH); // PC1 -> ETH_MDC
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource2, GPIO_AF_ETH); // PC2 -> MII_TXD2
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource3, GPIO_AF_ETH); // PC3 -> MII_TX_CLK
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource4, GPIO_AF_ETH); // PC4 -> RXD0
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource5, GPIO_AF_ETH); // PC5 -> RXD1
                                
  /* 配置GPIOG引脚：PG11（TX_EN）、PG13（TXD0）、PG14（TXD1） */
  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_14;
  GPIO_Init(GPIOG, &GPIO_InitStructure);
  GPIO_PinAFConfig(GPIOG, GPIO_PinSource11, GPIO_AF_ETH); // PG11 -> TX_EN
  GPIO_PinAFConfig(GPIOG, GPIO_PinSource13, GPIO_AF_ETH); // PG13 -> TXD0
  GPIO_PinAFConfig(GPIOG, GPIO_PinSource14, GPIO_AF_ETH); // PG14 -> TXD1

  /* 配置GPIOH引脚：PH2（MII_CRS）、PH3（MII_COL）、PH6（MII_RXD2）、PH7（MII_RXD3） */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_6 | GPIO_Pin_7;
  GPIO_Init(GPIOH, &GPIO_InitStructure);
  GPIO_PinAFConfig(GPIOH, GPIO_PinSource2, GPIO_AF_ETH); // PH2 -> MII_CRS
  GPIO_PinAFConfig(GPIOH, GPIO_PinSource3, GPIO_AF_ETH); // PH3 -> MII_COL
  GPIO_PinAFConfig(GPIOH, GPIO_PinSource6, GPIO_AF_ETH); // PH6 -> MII_RXD2
  GPIO_PinAFConfig(GPIOH, GPIO_PinSource7, GPIO_AF_ETH); // PH7 -> MII_RXD3

  /* 配置GPIOI引脚：PI10（MII_RX_ER） */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  GPIO_Init(GPIOI, &GPIO_InitStructure);
  GPIO_PinAFConfig(GPIOI, GPIO_PinSource10, GPIO_AF_ETH); // PI10 -> MII_RX_ER
}

/**
  * @brief  配置并使能以太网全局中断
  * @note   以太网中断包含数据收发完成、错误、DMA事件等，统一通过ETH_IRQn触发
  * @param  None
  * @retval None
  */
void ETH_NVIC_Config(void)
{
  NVIC_InitTypeDef   NVIC_InitStructure; // NVIC初始化结构体
  
  /* 配置以太网全局中断（ETH_IRQn）参数 */
  NVIC_InitStructure.NVIC_IRQChannel = ETH_IRQn; // 中断通道：以太网全局中断
  // 抢占优先级：12（数值越小优先级越高，需根据系统其他中断调整，避免抢占关键任务）
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 12 ;
  // 子优先级：0（抢占优先级相同时，子优先级决定响应顺序）
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能该中断通道
  NVIC_Init(&NVIC_InitStructure); // 应用配置到NVIC控制器
}

/**
  * @brief  配置PHY芯片，使其在链路状态变化时产生中断
  * @param  PHYAddress: 外部PHY芯片的地址（DP83848默认地址通常为0x01）
  * @retval ETH_SUCCESS: 配置成功；ETH_ERROR: 配置失败（超时等）
  */
uint32_t Eth_Link_PHYITConfig(uint16_t PHYAddress)
{
  uint16_t tmpreg = 0; // 临时存储PHY寄存器值

  /* 第一步：配置PHY中断控制寄存器（MICR）——使能中断输出 */
  // 读取PHY中断控制寄存器（MICR，地址：PHY_MICR）
  tmpreg = ETH_ReadPHYRegister(PHYAddress, PHY_MICR);
  // 设置MICR寄存器：
  // 1. PHY_MICR_INT_EN：使能PHY内部中断（允许PHY产生中断事件）
  // 2. PHY_MICR_INT_OE：使能PHY中断输出（将中断信号通过INT引脚发送给MCU）
  tmpreg |= (uint32_t)PHY_MICR_INT_EN | PHY_MICR_INT_OE;
  // 将配置写入PHY的MICR寄存器（超时会返回失败）
  if(!(ETH_WritePHYRegister(PHYAddress, PHY_MICR, tmpreg)))
  {
    /* 写入超时，返回错误 */
    return ETH_ERROR;
  }

  /* 第二步：配置PHY中断状态寄存器（MISR）——使能链路状态中断源 */
  // 读取PHY中断状态寄存器（MISR，地址：PHY_MISR）
  tmpreg = ETH_ReadPHYRegister(PHYAddress, PHY_MISR);
  // 设置MISR寄存器：PHY_MISR_LINK_INT_EN——使能"链路状态变化"中断
  // （仅该中断源被使能时，链路变化才会触发PHY中断）
  tmpreg |= (uint32_t)PHY_MISR_LINK_INT_EN;
  // 将配置写入PHY的MISR寄存器（超时会返回失败）
  if(!(ETH_WritePHYRegister(PHYAddress, PHY_MISR, tmpreg)))
  {
    /* 写入超时，返回错误 */
    return ETH_ERROR;
  }

  /* 配置成功，返回成功标志 */
  return ETH_SUCCESS;
}

/**
  * @brief  配置外部中断（EXTI）用于监听以太网链路状态变化
  * @note   功能逻辑：PHY芯片检测到链路变化时，会通过INT引脚输出电平信号，
  *         该信号触发MCU的EXTI中断，进而通知系统处理链路事件
  * @param  PHYAddress: 外部PHY芯片地址（注：此参数在当前函数实现中未使用，可能为预留扩展）
  * @retval None
  */
void Eth_Link_EXTIConfig(void)
{
  // 定义初始化结构体：分别用于GPIO、EXTI、NVIC配置
  GPIO_InitTypeDef GPIO_InitStructure;  // GPIO初始化结构体（配置PHY_INT引脚）
  EXTI_InitTypeDef EXTI_InitStructure;  // EXTI初始化结构体（配置外部中断参数）
  NVIC_InitTypeDef NVIC_InitStructure;  // NVIC初始化结构体（配置中断优先级）

  /* 1. 使能相关时钟：确保GPIO和SYSCFG外设可操作 */
  // 使能PHY_INT引脚对应的GPIO端口时钟（由宏ETH_LINK_GPIO_CLK指定，如GPIOB时钟）
  RCC_AHB1PeriphClockCmd(ETH_LINK_GPIO_CLK, ENABLE);
  // 使能SYSCFG时钟：SYSCFG负责将GPIO引脚映射到EXTI线路，必须开启
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

  /* 2. 配置PHY_INT引脚为输入模式：接收PHY芯片的中断信号 */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;        // 模式：输入（仅接收电平信号）
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;    // 上下拉：无（PHY_INT通常为开漏输出，外部电路需上拉）
  GPIO_InitStructure.GPIO_Pin = ETH_LINK_PIN;         // 目标引脚：由宏ETH_LINK_PIN指定（如PB14）
  GPIO_Init(ETH_LINK_GPIO_PORT, &GPIO_InitStructure); // 应用配置到指定GPIO端口（如GPIOB）

  /* 3. 关联GPIO引脚与EXTI线路：建立"引脚电平变化→EXTI中断"的映射关系 */
  // 参数1：GPIO端口源（由宏ETH_LINK_EXTI_PORT_SOURCE指定，如GPIO_PortSourceGPIOB）
  // 参数2：GPIO引脚源（由宏ETH_LINK_EXTI_PIN_SOURCE指定，如GPIO_PinSource14）
  // 作用：将指定GPIO引脚的电平变化事件，绑定到对应的EXTI线路（如PB14→EXTI14）
  SYSCFG_EXTILineConfig(ETH_LINK_EXTI_PORT_SOURCE, ETH_LINK_EXTI_PIN_SOURCE);

  /* 4. 配置EXTI线路：定义中断触发方式和工作模式 */
  EXTI_InitStructure.EXTI_Line = ETH_LINK_EXTI_LINE;  // 目标EXTI线路（如EXTI14，与引脚对应）
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 模式：中断模式（区别于事件模式）
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 触发方式：下降沿触发（PHY_INT通常为高电平变低电平表示中断）
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;           // 使能该EXTI线路
  EXTI_Init(&EXTI_InitStructure);                     // 应用配置到EXTI控制器

  /* 5. 配置NVIC：使能EXTI中断并设置优先级，确保CPU能响应中断 */
  // 中断通道：EXTI15_10_IRQn（EXTI10~15共用一个中断通道，需在中断服务函数中区分具体线路）
  NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
  // 抢占优先级：13（数值越小优先级越高，需根据系统其他中断调整，避免抢占关键任务如系统时钟）
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 13;
  // 子优先级：0（抢占优先级相同时，子优先级决定中断响应顺序）
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;      // 使能该NVIC中断通道
  NVIC_Init(&NVIC_InitStructure);                     // 应用配置到NVIC控制器
}

/**
  * @brief  FreeRTOS 任务：处理以太网链路状态变化（如链路通断）
  * @note   任务逻辑：等待链路中断信号量 → 检测 PHY 链路状态 → 更新 LwIP 网络接口状态
  * @param  pvParameters: 任务入口参数（此处传入 PHY 芯片地址，类型为 void* 符合 FreeRTOS 任务参数规范）
  * @retval None（FreeRTOS 任务函数无返回值，通过无限循环持续运行）
  */
void Eth_Link_IT_task( void * pvParameters )
{
  uint32_t pcPHYAddress;  // 存储 PHY 芯片地址（从任务参数转换而来）
  
  /* 1. 解析任务入口参数：将 void* 类型的 PHY 地址转换为 uint32_t 类型 */
  // pvParameters 是任务创建时传入的参数（见 ETH_BSP_Config 中 xTaskCreate 的第四个参数）
  // 此处传入的是 DP83848 PHY 芯片的地址，用于后续读取 PHY 寄存器
  pcPHYAddress = ( uint32_t  ) pvParameters;
  
  /* 2. 任务无限循环：FreeRTOS 任务需持续运行，不可退出 */
  for(;;)
  {
    /* 3. 等待以太网链路信号量（ETH_link_xSemaphore） */
    // xSemaphoreTake：获取二值信号量，用于同步（由链路中断服务函数释放信号量）
    // 参数1：待获取的信号量（ETH_link_xSemaphore，在 ETH_BSP_Config 中创建）
    // 参数2：等待超时时间（emacBLOCK_TIME_WAITING_ETH_LINK_IT = 100ms，超时返回 pdFALSE）
    // 返回值：pdTRUE 表示成功获取信号量（即有链路状态变化中断触发）
    if (xSemaphoreTake( ETH_link_xSemaphore, emacBLOCK_TIME_WAITING_ETH_LINK_IT ) == pdTRUE)
    {
      /* 4. 验证是否为链路状态变化中断（避免其他中断误触发） */
      // 步骤1：读取 PHY 中断状态寄存器（PHY_MISR），判断是否为链路状态中断
      // PHY_MISR：PHY 中断状态寄存器，存储各中断源的触发状态
      // PHY_LINK_STATUS：链路状态中断标志位（宏定义，对应 PHY_MISR 中表示链路变化的位）
      if ( ( ETH_ReadPHYRegister( (uint16_t) pcPHYAddress, PHY_MISR ) & PHY_LINK_STATUS ) != 0 )
      {
        /* 5. 读取 PHY 状态寄存器（PHY_SR），获取当前实际链路状态 */
        // PHY_SR：PHY 状态寄存器，bit0 通常表示链路是否连通（1=连通，0=断开）
        // 此处通过 "& 1" 判断 bit0 的值，即当前链路是通还是断
        if ( ( ETH_ReadPHYRegister( (uint16_t) pcPHYAddress, PHY_SR ) & 1 ) != 0 )
        {
          /* 6. 链路已连通：调用 LwIP 接口函数设置网络接口为"链路_up"状态 */
          // netif_set_link_up：LwIP 核心函数，通知协议栈链路已接通
          // 效果：LwIP 会恢复数据收发、若开启 DHCP 会尝试重新获取 IP 地址等
          netif_set_link_up( &xnetif );
        }
        else
        {
          /* 7. 链路已断开：调用 LwIP 接口函数设置网络接口为"链路_down"状态 */
          // netif_set_link_down：LwIP 核心函数，通知协议栈链路已断开
          // 效果：LwIP 会停止数据收发、标记网络不可用等
          netif_set_link_down( &xnetif );
        }
      }
    }
    // 若信号量获取超时（100ms 内无中断），则重新进入循环等待，不执行任何操作
  }
}

/**
  * @brief  以太网链路状态变化时的回调函数
  * @note   当链路接通或断开时，LwIP 会调用此函数进行相应处理
  *         1. 链路接通：重新启动 PHY 自动协商 → 更新 MAC 速率/双工模式 → 启动 MAC → 配置 IP 地址 → 启动网络接口
  *         2. 链路断开：停止 MAC → 停止 DHCP（如果使用） → 关闭网络接口
  * @param  netif: 指向 LwIP 网络接口结构体的指针
  * @retval None
  */
void ETH_link_callback(struct netif *netif)
{
  __IO uint32_t timeout = 0;   // 超时计数器
  uint32_t tmpreg, RegValue;   // 用于存储寄存器值

  struct ip_addr ipaddr;       // IP 地址结构体
  struct ip_addr netmask;      // 子网掩码结构体
  struct ip_addr gw;           // 网关结构体
#ifndef USE_DHCP
  uint8_t iptab[4] = {0};      // 存储 IP 地址的字节数组
  uint8_t iptxt[20];           // 格式化后的 IP 地址字符串
#endif /* USE_DHCP */

  /* 判断链路是否接通 */
  if (netif_is_link_up(netif))
  {
    /* 如果链路接通 */

    /* 如果 PHY 开启了自动协商功能 */
    if (ETH_InitStructure.ETH_AutoNegotiation != ETH_AutoNegotiation_Disable)
    {
      /* 重启自动协商 */
      timeout = 0;
      ETH_WritePHYRegister(DP83848_PHY_ADDRESS, PHY_BCR, PHY_AutoNegotiation);

      /* 等待自动协商完成 */
      do
      {
        timeout++;
      } while (!(ETH_ReadPHYRegister(DP83848_PHY_ADDRESS, PHY_BSR) & PHY_AutoNego_Complete) && 
               (timeout < (uint32_t)PHY_READ_TO));

      /* 读取自动协商结果（PHY 状态寄存器） */
      RegValue = ETH_ReadPHYRegister(DP83848_PHY_ADDRESS, PHY_SR);
    
      /* 根据协商结果设置 MAC 双工模式 */
      if ((RegValue & PHY_DUPLEX_STATUS) != (uint32_t)RESET)
      {
        ETH_InitStructure.ETH_Mode = ETH_Mode_FullDuplex;  // 全双工
      }
      else
      {
        ETH_InitStructure.ETH_Mode = ETH_Mode_HalfDuplex;  // 半双工
      }

      /* 根据协商结果设置 MAC 速率 */
      if (RegValue & PHY_SPEED_STATUS)
      {
        ETH_InitStructure.ETH_Speed = ETH_Speed_10M;  // 10M
      }
      else
      {
        ETH_InitStructure.ETH_Speed = ETH_Speed_100M; // 100M
      }

      /* 更新 MAC 控制寄存器 MACCR */
      tmpreg = ETH->MACCR;
      tmpreg |= (uint32_t)(ETH_InitStructure.ETH_Speed | ETH_InitStructure.ETH_Mode);
      ETH->MACCR = (uint32_t)tmpreg;

      _eth_delay_(ETH_REG_WRITE_DELAY);
      tmpreg = ETH->MACCR;
      ETH->MACCR = tmpreg;
    }

    /* 启动 MAC 接收/发送 */
    ETH_Start();

#ifdef USE_DHCP
    /* DHCP 模式：清空 IP 配置，启动 DHCP */
    ipaddr.addr = 0;
    netmask.addr = 0;
    gw.addr = 0;

    DHCP_state = DHCP_START; // 设置 DHCP 状态为启动
#else
    /* 静态 IP 模式：配置 IP 地址、子网掩码、网关 */
    IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
    IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1 , NETMASK_ADDR2, NETMASK_ADDR3);
    IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
#endif /* USE_DHCP */

    /* 设置网络接口地址 */
    netif_set_addr(&xnetif, &ipaddr , &netmask, &gw);
    
    /* 标记网络接口为可用 */
    netif_set_up(&xnetif);    

#ifndef USE_DHCP
    /* 显示静态 IP 地址 */
    iptab[0] = IP_ADDR3;
    iptab[1] = IP_ADDR2;
    iptab[2] = IP_ADDR1;
    iptab[3] = IP_ADDR0;
    sprintf((char*)iptxt, "  %d.%d.%d.%d", iptab[3], iptab[2], iptab[1], iptab[0]); 
#endif /* USE_DHCP */
  }
  else
  {
    /* 如果链路断开 */

    /* 停止 MAC 接收/发送 */
    ETH_Stop();

#ifdef USE_DHCP
    /* 停止 DHCP 客户端 */
    DHCP_state = DHCP_LINK_DOWN;
    dhcp_stop(netif);
#endif /* USE_DHCP */

    /* 标记网络接口为不可用 */
    netif_set_down(&xnetif);
  }
}

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
